// CSkinableGUIWnd.cpp : implementation file
//

#include "stdafx.h"
#include "SkinableGUIWnd.h"

#include "SkinManager.h"
#include "SkinableGUIBitmapDC.h"

#include <memory>

using namespace SkinableGUI;
// CSkinableGUIWnd

IMPLEMENT_DYNAMIC(CSkinableGUIWnd, CWnd)

CSkinableGUIWnd::CSkinableGUIWnd(const CString & pSkinFile) :
m_strSkinXmlPath(pSkinFile)
, m_bDestroySkinControl(FALSE)
, m_pBackgroundImage(NULL)
, m_nBackgroundWidth(0)
, m_nBackgroundHeight(0)
, m_pSavedBackgroundBmp(NULL)
{
	CSkinManager * pManager = CSkinManager::GetInstance();

	std::auto_ptr<SkinWindow> window(new SkinWindow());

	if (!pManager->LoadXmlSkinFile(m_strSkinXmlPath, *window))
	{
		CString msg;

		msg.Format(_T("Unable to Load skin file %s"), 
			m_strSkinXmlPath);

		AfxMessageBox(msg);
	}
	else
	{
		m_pSkinControl = window.release();
		m_bDestroySkinControl = TRUE;
	}
}

CSkinableGUIWnd::CSkinableGUIWnd(SkinControl * pSkinControl) :
m_pSkinControl(pSkinControl)
, m_bDestroySkinControl(FALSE)
, m_pBackgroundImage(NULL)
, m_nBackgroundWidth(0)
, m_nBackgroundHeight(0)
, m_pSavedBackgroundBmp(NULL)
{
}

CSkinableGUIWnd::~CSkinableGUIWnd()
{
	if (m_bDestroySkinControl)
		delete m_pSkinControl;
}


BEGIN_MESSAGE_MAP(CSkinableGUIWnd, CWnd)
	ON_WM_CREATE()
	ON_WM_DESTROY()
	ON_WM_ERASEBKGND()
	ON_WM_PAINT()
	ON_WM_SIZE()
	ON_WM_KEYUP()
	ON_WM_SETFOCUS()
	ON_WM_GETDLGCODE()
END_MESSAGE_MAP()



// CSkinableGUIWnd message handlers
BOOL CSkinableGUIWnd::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext)
{
	// TODO: Add your specialized code here and/or call the base class

	return CWnd::Create(_T("SkinableGUIWindowClass"), lpszWindowName, dwStyle, rect, pParentWnd, nID, pContext);
}

BOOL CSkinableGUIWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, LPVOID lpParam)
{
	// TODO: Add your specialized code here and/or call the base class

	return CWnd::CreateEx(dwExStyle, _T("SkinableGUIWindowClass"), lpszWindowName, dwStyle, rect, pParentWnd, nID, lpParam);
}

void CSkinableGUIWnd::CalcWindowRect(LPRECT lpClientRect, UINT nAdjustType)
{
	// TODO: Add your specialized code here and/or call the base class

	CWnd::CalcWindowRect(lpClientRect, nAdjustType);
}

LRESULT CSkinableGUIWnd::DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
	// TODO: Add your specialized code here and/or call the base class

	return CWnd::DefWindowProc(message, wParam, lParam);
}

BOOL CSkinableGUIWnd::DestroyWindow()
{
	// TODO: Add your specialized code here and/or call the base class

	return CWnd::DestroyWindow();
}

BOOL CSkinableGUIWnd::OnChildNotify(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pLResult)
{
	// TODO: Add your specialized code here and/or call the base class

	return CWnd::OnChildNotify(message, wParam, lParam, pLResult);
}

BOOL CSkinableGUIWnd::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
	// TODO: Add your specialized code here and/or call the base class

	return CWnd::OnNotify(wParam, lParam, pResult);
}

void CSkinableGUIWnd::PostNcDestroy()
{
	CWnd::PostNcDestroy();
}

BOOL CSkinableGUIWnd::PreCreateWindow(CREATESTRUCT& cs)
{
	return CWnd::PreCreateWindow(cs);
}

int CSkinableGUIWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CWnd::OnCreate(lpCreateStruct) == -1)
		return -1;

	if (m_pSkinControl == NULL)
		return -1;

	//Modifiy Styles
	CSkinManager * pManager = CSkinManager::GetInstance();

	if (m_pSkinControl->ControlType == SkinableGUI::Window)
	{
		SkinWindow * pSkinWindow = (SkinWindow *)m_pSkinControl;

		if (pSkinWindow->WindowStyleSet)
		{
			if (!pManager->UpdateWindowStyle(this, pSkinWindow->WindowStyle))
			{
				return -1;
			}
		}

		if (pSkinWindow->TitleSet)
			SetWindowText(pSkinWindow->Title);
	}

	if (m_pSkinControl->StyleSet)
	{
		if (!pManager->UpdateControlStyle(this, m_pSkinControl->Style))
		{
			return -1;
		}
	}

	if (m_pSkinControl->BackgroundImageSet)
	{
		//Load background image
		if (!pManager->LoadImage(m_pSkinControl->BackgroundImage,
			m_BackgroundImage))
		{
			return -1;
		}
	}

	if (m_pSkinControl->BackgroundColorSet)
	{
		//Load background color
		m_BackgroundColor = RGB(m_pSkinControl->BackgroundColor.R,
			m_pSkinControl->BackgroundColor.G,
			m_pSkinControl->BackgroundColor.B);
	}

	//Create child controls
	SkinControl::ControlList::iterator it;

	for(it = m_pSkinControl->Controls.begin(); it != m_pSkinControl->Controls.end(); it++)
	{
		SkinControl * pSkinControl = *it;

		if (pSkinControl->ControlType == SkinableGUI::Picture)
		{
			SkinPictureControl * pictureControl = 
				dynamic_cast<SkinPictureControl *>(pSkinControl);

			std::auto_ptr<CSkinableGUIImage> image(new CSkinableGUIImage());

			if (pManager->LoadImage(pictureControl->Image,
				*image))
			{
				image->SetDefaultRect(pictureControl->Position);
				m_Pictures.push_back(image.release());
			}
		}
		else
		{
			CWnd * pWnd = pManager->CreateControl(this,
				*pSkinControl);

			if (NULL != pWnd)
			{
				ChildWindow cw;
				cw.pWnd = pWnd;
				cw.pSkinControl = pSkinControl;

				m_ChildrenWindows.push_back(cw);
				m_ChildrenWindowsMap[pSkinControl->Id] =
					cw;
			}
		}//if picture type
	}//for each controls

	return 0;
}

void CSkinableGUIWnd::OnDestroy()
{
	CWnd::OnDestroy();

	CChildWindowList::iterator it;

	for(it = m_ChildrenWindows.begin(); it != m_ChildrenWindows.end(); it++)
	{
		CWnd * pChild = (*it).pWnd;

		pChild->DestroyWindow();
		delete pChild;
	}

	m_ChildrenWindows.clear();
	m_ChildrenWindowsMap.clear();

	DestroyBackgroundPainter();
}

BOOL CSkinableGUIWnd::OnEraseBkgnd(CDC* pDC)
{
	//return CWnd::OnEraseBkgnd(pDC);
	return TRUE;
}

void CSkinableGUIWnd::OnPaint()
{
	CPaintDC dc(this); // device context for painting

	DoWndPaint(&dc);
}

void CSkinableGUIWnd::DoWndPaint(CDC * pDC)
{
	CRect rcClient;
	GetClientRect(&rcClient);

	CRect rcClip;

	pDC->GetClipBox(&rcClip);

	if (m_pBackgroundImage == NULL)
	{
		PrepareBackgroundPainter(pDC, rcClient);
		rcClip = rcClient;
		
		CRgn rgn;
		rgn.CreateRectRgn(rcClient.left, rcClient.top,
			rcClient.right, rcClient.bottom);

		pDC->SelectClipRgn(&rgn, RGN_OR);
	}

	CSkinableGUIBitmapDC dc(pDC,  m_pBackgroundImage);

	if (m_pSkinControl->TransparentBackgroundSet && 
		m_pSkinControl->TransparentBackground)
	{
		pDC->TransparentBlt(rcClip.left, rcClip.top,
			rcClip.Width(), rcClip.Height(),
			&dc, rcClip.left, rcClip.top, 
			rcClip.Width(), rcClip.Height(),
			GetSysColor(COLOR_WINDOW));
	}
	else
	{
		pDC->BitBlt(rcClip.left, rcClip.top,
			rcClip.Width(), rcClip.Height(),
			&dc, rcClip.left, rcClip.top, SRCCOPY);
	}

	CChildWindowList::iterator child_it;

	rcClip.NormalizeRect();

	for(child_it = m_ChildrenWindows.begin(); 
		child_it != m_ChildrenWindows.end(); 
		child_it++)
	{
		CWnd * pChild = (*child_it).pWnd;

		if (!pChild->IsWindowVisible())
		{
			pChild->ShowWindow(SW_SHOW);
		}

		pChild->Invalidate();
		pChild->UpdateWindow();
	}
}

void CSkinableGUIWnd::OnSize(UINT nType, int cx, int cy)
{
	CWnd::OnSize(nType, cx, cy);

	if (::IsWindow(GetSafeHwnd()))
	{
		DestroyBackgroundPainter();
	}

	CChildWindowList::iterator it;

	for(it = m_ChildrenWindows.begin(); it != m_ChildrenWindows.end(); it++)
	{
		CWnd * pChild = (*it).pWnd;

		if (::IsWindow(pChild->GetSafeHwnd()))
		{
			CRect rcChild = (*it).pSkinControl->Position;

			SkinRectToClient(rcChild);

			pChild->MoveWindow(&rcChild, FALSE);

			pChild->GetClientRect(&rcChild);
			pChild->InvalidateRect(&rcChild);
		}
	}
}

void CSkinableGUIWnd::SkinRectToClient(CRect & rc)
{
	CRect rcClient;

	GetClientRect(&rcClient);

	double x_delta = 
		(double)rcClient.Width() / (double)m_pSkinControl->Position.Width();
	double y_delta = 
		(double)rcClient.Height() / (double)m_pSkinControl->Position.Height();

	rc.left = (int)(rc.left * x_delta);
	rc.right = (int)(rc.right * x_delta);
	rc.top = (int)(rc.top * y_delta);
	rc.bottom = (int)(rc.bottom * y_delta);
}

void SkinableGUI::CSkinableGUIWnd::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	CWnd::OnKeyUp(nChar, nRepCnt, nFlags);

	CWnd * pWnd = GetParent();

	if (pWnd != NULL)
	{
		pWnd->PostMessage(WM_KEYUP,
			MAKEWPARAM(nChar,0),
			MAKELPARAM(nRepCnt, nFlags));
	}
}

void CSkinableGUIWnd::DestroyBackgroundPainter()
{
	if (NULL != m_pBackgroundImage)
	{
		delete m_pBackgroundImage;

		m_pBackgroundImage = NULL;
	}

	if (NULL != m_pSavedBackgroundBmp)
		delete m_pSavedBackgroundBmp;

	m_pSavedBackgroundBmp = NULL;
}

void CSkinableGUIWnd::PrepareBackgroundPainter(CDC * pDC, 
											   const CRect & rcBackground)
{
	m_nBackgroundWidth = rcBackground.Width();
	m_nBackgroundHeight = rcBackground.Height();

	CRect rcDest(rcBackground);

	rcDest.MoveToXY(0,0);

	m_pBackgroundImage = new CBitmap();

	m_pBackgroundImage->CreateCompatibleBitmap(pDC, 
		rcBackground.Width(), rcBackground.Height());

	CSkinableGUIBitmapDC tmpDC(pDC, m_pBackgroundImage);

	if (m_pSkinControl->BackgroundImageSet)
	{
		m_BackgroundImage.Blt(&tmpDC, &rcDest);
	}
	else if (m_pSkinControl->BackgroundColorSet)
	{
		tmpDC.FillSolidRect(&rcDest, m_BackgroundColor);
	}
	else if (m_pSkinControl->TransparentBackgroundSet && 
		m_pSkinControl->TransparentBackground)
	{
		if (NULL == m_pSavedBackgroundBmp)
		{
			m_pSavedBackgroundBmp = new CBitmap();
			m_pSavedBackgroundBmp->CreateCompatibleBitmap(pDC,
				rcBackground.Width(), rcBackground.Height());
		
			CSkinableGUIBitmapDC savedDC(pDC, m_pSavedBackgroundBmp);
			savedDC.BitBlt(0,0,
				rcBackground.Width(), rcBackground.Height(),
				pDC,
				rcBackground.left, rcBackground.top,
				SRCCOPY);
		}

		CSkinableGUIBitmapDC savedDC(pDC, m_pSavedBackgroundBmp);

		//tmpDC.BitBlt(0,0,
		//	rcBackground.Width(), rcBackground.Height(),
		//	&savedDC,
		//	rcBackground.left, rcBackground.top,
		//	SRCCOPY);
		tmpDC.FillSolidRect(&rcDest, GetSysColor(COLOR_WINDOW));
	}
	else
	{
		tmpDC.FillSolidRect(&rcDest, GetSysColor(COLOR_WINDOW));
	}

	CSkinableGUIImageList::iterator it;

	for(it = m_Pictures.begin(); it != m_Pictures.end(); it++)
	{
		CSkinableGUIImage * pImage = *it;

		CRect rcImage = pImage->GetDefaultRect();

		SkinRectToClient(rcImage);

		pImage->Blt(&tmpDC, &rcImage);
	}
}

BOOL SkinableGUI::CSkinableGUIWnd::CreateSkinFont(CFont & font, COLORREF & txtColor, COLORREF & bkColor)
{
	SkinControl * pSkinControl = m_pSkinControl;

	CSkinManager * pManager = CSkinManager::GetInstance();

	while(pSkinControl != NULL)
	{
		if (pSkinControl->FontSet)
		{
			CFont tmpFont;

			//Load font
			if (pManager->LoadFont(pSkinControl->Font,
				tmpFont))
			{
				LOGFONT lgFt;

				tmpFont.GetLogFont(&lgFt);

				CRect rcFt(0,0,lgFt.lfHeight, lgFt.lfHeight);

				SkinRectToClient(rcFt);

				lgFt.lfHeight = rcFt.Height();

				font.CreateFontIndirect(&lgFt);

				if (pSkinControl->Font.TextColorSet)
				{
					txtColor = pSkinControl->Font.TextColor.ToColor();
				}
				else
				{
					txtColor = (DWORD)-1;
				}

				if (pSkinControl->Font.BackgroundColorSet)
				{
					bkColor = pSkinControl->Font.BackgroundColor.ToColor();
				}
				else
				{
					bkColor = (DWORD)-1;
				}

				return TRUE;
			}
		}

		pSkinControl = pSkinControl->ParentControl;
	}

	return FALSE;
}

void SkinableGUI::CSkinableGUIWnd::OnSetFocus(CWnd* pOldWnd)
{
	CWnd::OnSetFocus(pOldWnd);

	if (m_ChildrenWindows.size() > 0)
	{
		CWnd * pWnd = (*m_ChildrenWindows.begin()).pWnd;

		if (::IsWindow(pWnd->GetSafeHwnd()))
		{
			pWnd->SetForegroundWindow();
			pWnd->SetFocus();
		}
	}
}

UINT SkinableGUI::CSkinableGUIWnd::OnGetDlgCode()
{
	return  DLGC_WANTALLKEYS;
}

CWnd * CSkinableGUIWnd::GetChildWnd(UINT nID)
{
	CChildWindowMap::iterator it =
		m_ChildrenWindowsMap.find(nID);

	if (it != m_ChildrenWindowsMap.end())
		return it->second.pWnd;

	return NULL;
}

void CSkinableGUIWnd::Invalidate(BOOL bErase)
{
	CWnd::Invalidate(bErase);
}

void CSkinableGUIWnd::InvalidateRect(const RECT * lpRect, BOOL bErase)
{
	CWnd::InvalidateRect(lpRect, bErase);
}

void CSkinableGUIWnd::DoRefreshWindow(const RECT * lpRect, BOOL bErase)
{
	if (m_pSkinControl->TransparentBackgroundSet && 
		m_pSkinControl->TransparentBackground)
	{
		CWnd * pParent = GetParent();

		if (pParent != NULL)
		{
			CRect rcParent;

			if (NULL == lpRect)
			{
				GetClientRect(&rcParent);
			}
			else
			{
				rcParent = *lpRect;
			}

			ClientToScreen(&rcParent);
			pParent->ScreenToClient(&rcParent);
			pParent->InvalidateRect(&rcParent);
			pParent->UpdateWindow();

			return;
		}
	}

	if (NULL == lpRect)
	{
		Invalidate(bErase);
		UpdateWindow();
	}
	else
	{
		InvalidateRect(lpRect, bErase);
	}
}
